package validate

import (
	"strconv"
	"strings"
	"unicode/utf8"
)

// IsInt 字符串是否为INT或UINT数字(支持十进制格式, 支持科学计数法)
// 允许0，允许负数
func IsInt(val string) bool {
	return IsIntWith(val, 0, true)
}

// IsUint 字符串是否为UINT数字(支持十进制格式, 支持科学计数法)
// 允许0，不允许负数
func IsUint(val string) bool {
	return IsIntWith(val, 0, false)
}

// IsIntWith 字符串是否为Float数字(支持十进制格式, 支持科学计数法)
//   - val 字符串
//   - digit INT位数 0/不限制 8 16 32 64...
//   - isNegative 是否允许负数
func IsIntWith(val string, digit int, isNegative bool) bool {
	// 是否是科学计数法
	if strings.Contains(val, "e") || strings.Contains(val, "E") {
		// 作为科学计数法处理
		return IsIntWithSN(val, digit, isNegative)
	}
	// 作为十进制格式处理
	return IsIntWithDecimal(val, digit, isNegative)
}

// 快速判断1-2位数 INT
//   - val 字符串，调用入口已限制 len(val) = 1 或 2
func _isIntQuik(val string, isNegative bool) bool {
	if len(val) == 1 {
		return val[0] >= '0' && val[0] <= '9'
	}
	// 快速判断1-2位数 INT
	if val[0] > '0' && val[0] <= '9' {
		if val[1] >= '0' && val[1] <= '9' {
			return true
		}
	}
	// +[0-9]
	if val[0] == '+' {
		if val[1] >= '0' && val[1] <= '9' {
			return true
		}
	}
	// -[0-9]
	if isNegative && val[0] == '-' {
		if val[1] >= '0' && val[1] <= '9' {
			return true
		}
	}
	// 非INT([0-9].)或其他字符不能表示为有效数字，直接返回
	return false
}

// IsIntWithDecimal 字符串是否为INT或UINT数字(仅支持十进制格式)
//   - val 字符串
//   - digit INT位数 0/不限制 8 16 32 64...
//   - isNegative 是否允许负数
func IsIntWithDecimal(val string, digit int, isNegative bool) bool {
	// 快速检查
	// - 字符串长度小于1
	// - 首字符不能是0
	// - 不能仅为一个负号或正号
	// - 不能包含小数点
	// - 不能包含空格
	// - 包含非Ascii字符
	l := len(val)
	if l < 1 {
		return false
	}
	if l < 3 {
		// 快速判断1-2位数 INT, 非INT([0-9].)或其他字符不能表示为有效数字，直接返回
		return _isIntQuik(val, isNegative)
	}
	if val[0] == '0' || // 首字符不能是0
		val == "-" || // 不能仅为一个负号
		val == "+" || // 不能仅为一个正号
		strings.Contains(val, ".") || // 不能包含小数点
		strings.Contains(val, " ") || // 不能包含空格
		utf8.RuneCountInString(val) != l { // 长度不符, 包含非Ascii字符
		return false
	}

	// ASCII 43 DEC +
	// ASCII 45 DEC -
	// ASCII 46 DEC .
	// ASCII 48---57  DEC 0---9
	//遍历每一个ASCII字符
	i := 0
	//-负号
	if val[0] == 45 {
		if !isNegative {
			//不允许负数
			return false
		}
		i++
		if i == l {
			//只有一个负号
			return false
		}
	}
	// 首数字只能是 1-9
	if val[i] < 49 || val[i] > 57 {
		return false
	}
	// 其余数字 0-9
	for ; i < l; i++ {
		if val[i] < 48 || val[i] > 57 {
			return false
		}
	}
	//长度不相符（不含有特殊字符）
	if l != i {
		return false
	}
	if digit > 0 {
		if digit == 64 {
			if isNegative {
				if _, err := strconv.ParseInt(val, 10, 64); err != nil {
					return false
				}
			} else {
				if _, err := strconv.ParseUint(val, 10, 64); err != nil {
					return false
				}
			}
			return true
		}
		intN, err := strconv.Atoi(val)
		if err != nil {
			return false
		}
		switch digit {
		case 8:
			if isNegative {
				//-128 - 127
				return intN >= (-128) && intN <= 127
			} else {
				//0 - 255
				return intN >= 0 && intN <= 255
			}
		case 16:
			if isNegative {
				//-32768 - 32767
				return intN >= (-32768) && intN <= 32767
			} else {
				//0 - 65535
				return intN >= 0 && intN <= 65535
			}
		case 32:
			if isNegative {
				//-2147483648 - 2147483647
				return intN >= (-2147483648) && intN <= 2147483647
			} else {
				//0 - 4294967295
				return intN >= 0 && intN <= 4294967295
			}
		default:
			return false
		}
	}
	return true
}

// IsIntWithSN 字符串是否为INT或UINT数字(仅支持科学计数法)
//   - val 字符串
//   - digit INT位数 0/不限制 8 16 32 64...
//   - isNegative 是否允许负数
func IsIntWithSN(val string, digit int, isNegative bool) bool {
	// 快速检查
	// - 字符串长度小于1
	// - 首尾不能是小数点符号
	// - 不能仅为一个负号或正号
	// - 不能包含空格
	// - 最多只能有一个小数点
	// - 包含非Ascii字符
	l := len(val)
	if l < 1 {
		return false
	}
	if l < 3 {
		// 快速判断1-2位数 INT, 非INT([0-9].)或其他字符不能表示为有效数字，直接返回
		return _isIntQuik(val, isNegative)
	}
	if val[0] == '.' || // 首字符不能是.
		val[l-1] == '.' || // 尾字符不能是.
		val == "-" || // 不能仅为一个负号
		val == "+" || // 不能仅为一个正号
		strings.Contains(val, " ") || // 不能包含空格
		strings.Count(val, ".") > 1 || // 最多只能有一个小数点
		utf8.RuneCountInString(val) != l { // 长度不符, 包含非Ascii字符
		return false
	}
	// 不允许负数
	if !isNegative && val[0] == '-' {
		return false
	}
	// 开始检查每一个字符
	var (
		hasExponent      = false // 是否进入指数部分
		hasExponentDigit = false // 指数部分是否有数字
		hasDot           = false // 基数部分是否有小数点
	)
	i := 0
	// 正负值 - + 符号
	if val[i] == '-' || val[i] == '+' {
		i++
	}
	if val[i] == '0' {
		// 首位是0，第二位只能是小数点，第三位只能是数字
		i++
		if i == l-1 {
			return false
		}
		if val[i] != '.' {
			return false
		}
		i++
		if val[i] < '0' || val[i] > '9' {
			return false
		}
		hasDot = true
	} else {
		// 首位是必须是不为0的数字(基数部分)
		if val[i] < '1' || val[i] > '9' {
			return false
		}
		i++
	}

	val = strings.ToLower(val)
	for ; i < l; i++ {
		switch val[i] {
		case '+', '-':
			// 符号只能出现在指数部分开始的位置
			if i < 2 {
				return false
			}
			if val[i-1] == 'e' && hasExponent {
				continue
			}
			return false
		case '.':
			// 小数点只能在基数部分，且只能出现一次
			if hasExponent || hasDot {
				return false
			}
			hasDot = true
			continue
		case 'e':
			// 必须在基数部分之后
			if hasExponent {
				return false
			}
			hasExponent = true
			continue
		default:
			if val[i] < '0' || val[i] > '9' {
				return false
			} else {
				// 数字处理
				if hasExponent {
					hasExponentDigit = true
				}
			}
		}
	}
	// 最终检查
	if hasExponent && !hasExponentDigit {
		return false // exponent part has no digits
	}
	// 基数是否含有小数点 或 是否需要检查INT位数 8 16 32 64
	if digit > 0 || hasDot {
		strDecimal, ok := _matchNumberSNToDecimal(val)
		if !ok {
			return false
		}

		if hasDot && strings.Contains(strDecimal, ".") {
			// 转换后含有小数，非INT
			return false
		}
		if digit == 0 {
			// 不需要检查INT位数
			return true
		}

		if digit == 64 {
			if isNegative {
				if _, err := strconv.ParseInt(strDecimal, 10, 64); err != nil {
					return false
				}
			} else {
				if _, err := strconv.ParseUint(strDecimal, 10, 64); err != nil {
					return false
				}
			}
			return true
		}
		intN, err := strconv.Atoi(strDecimal)
		if err != nil {
			return false
		}
		switch digit {
		case 8:
			if isNegative {
				//-128 - 127
				return intN >= (-128) && intN <= 127
			} else {
				//0 - 255
				return intN >= 0 && intN <= 255
			}
		case 16:
			if isNegative {
				//-32768 - 32767
				return intN >= (-32768) && intN <= 32767
			} else {
				//0 - 65535
				return intN >= 0 && intN <= 65535
			}
		case 32:
			if isNegative {
				//-2147483648 - 2147483647
				return intN >= (-2147483648) && intN <= 2147483647
			} else {
				//0 - 4294967295
				return intN >= 0 && intN <= 4294967295
			}
		default:
			return false
		}

	}
	return true
}

// IsFloat 字符串是否为Float数字 (支持十进制格式, 支持科学计数法)
//   - 允许0，小数位数不限制(没有或任意)，允许负数
//   - 注意: INT 数字会被认为是Float
func IsFloat(val string) bool {
	return IsFloatWith(val, 0, true)
}

// IsUfloat 字符串是否为Float数字 (正Float, 支持十进制格式, 支持科学计数法)
//   - 允许0，小数位数不限制(没有或任意)，不允许负数
//   - 注意: INT 数字会被认为是Float
func IsUfloat(val string) bool {
	return IsFloatWith(val, 0, false)
}

// IsFloatWith 字符串是否为Float数字(支持十进制格式, 支持科学计数法)
//   - val 字符串
//   - decimal 参数为小数位数 0/不限制(没有或任意) 1/一位 2/二位...
//   - isNegative 是否允许负数
//   - 注意: INT 数字会被认为是Float
func IsFloatWith(val string, decimal int, isNegative bool) bool {
	// 是否是科学计数法
	if strings.Contains(val, "e") || strings.Contains(val, "E") {
		// 作为科学计数法处理
		return IsFloatWithSN(val, decimal, isNegative)
	}
	// 作为十进制格式处理
	return IsFloatWithDecimal(val, decimal, isNegative)
}

// IsFloatWithDecimal 字符串是否为Float数字(仅支持十进制格式)
//   - val 字符串
//   - decimal 参数为小数位数 0/不限制(没有或任意) 1/一位 2/二位...
//   - isNegative 是否允许负数
func IsFloatWithDecimal(val string, decimal int, isNegative bool) bool {
	// 快速检查
	// - 字符串长度小于1
	// - 首尾不能是小数点符号
	// - 不能仅为一个负号或正号
	// - 最多只能有一个小数点
	// - 不能包含空格
	// - 包含非Ascii字符
	l := len(val)
	if l < 1 {
		return false
	}
	if l < 3 && decimal == 0 {
		// 快速判断1-2位数 INT, 非INT([0-9].)或其他字符不能表示为有效数字，直接返回
		return _isIntQuik(val, isNegative)
	}

	if val[0] == '.' || // 首字符不能是.
		val[l-1] == '.' || // 尾字符不能是.
		val == "-" || // 不能仅为一个负号
		val == "+" || // 不能仅为一个正号
		strings.Count(val, ".") > 1 || // 最多只能有一个小数点
		strings.Contains(val, " ") || // 不能包含空格
		utf8.RuneCountInString(val) != l { // 长度不符, 包含非Ascii字符
		return false
	}
	// 不允许负数
	if !isNegative && val[0] == '-' {
		return false
	}
	// 检测小数位数
	if decimal > 0 {
		if !strings.Contains(val, ".") {
			return false
		}
		valS := strings.Split(val, ".")
		if len(valS) != 2 || len(valS[1]) != decimal {
			return false
		}
	}
	i := 0
	// - + 符号
	if val[0] == '-' || val[0] == '+' {
		i++
	}
	isDot := false
	// 首字符是0,第2个字符必须是. 第3个字符必须是数字
	if val[i] == '0' {
		i++
		if i > l-1 || val[i] != '.' {
			return false
		}
		i++
		if val[i] < '0' || val[i] > '9' {
			return false
		}
		isDot = true
	} else {
		// 首数字只能是 1-9
		if val[i] < 49 || val[i] > 57 {
			return false
		}
	}
	// 其余数字 0-9,!isDot 是还允许包含一个.
	for ; i < l; i++ {
		if val[i] == '.' {
			//是否已经检测了. 且.不能是最后一个字符
			if isDot {
				return false
			}
			isDot = true
			continue
		}
		if val[i] < '0' || val[i] > '9' {
			return false
		}
	}
	return true
}

// IsFloatWithSN 字符串是否为Float数字(仅支持科学计数法)
//   - val 字符串
//   - decimal 参数为小数位数 0/不限制(没有或任意) 1/一位 2/二位...
//   - isNegative 是否允许负数
func IsFloatWithSN(val string, decimal int, isNegative bool) bool {
	// 快速检查
	// - 字符串长度小于1
	// - 首尾不能是小数点符号
	// - 不能仅为一个负号或正号
	// - 最多只能有一个小数点
	// - 不能包含空格
	// - 包含非Ascii字符
	l := len(val)
	if l < 1 {
		return false
	}
	if l < 3 && decimal == 0 {
		// 快速判断1-2位数 INT, 非INT([0-9].)或其他字符不能表示为有效数字，直接返回
		return _isIntQuik(val, isNegative)
	}
	// 科学计数法至少有3位
	if val[0] == '.' || // 首字符不能是.
		val[l-1] == '.' || // 尾字符不能是.
		val == "-" || // 不能仅为一个负号
		val == "+" || // 不能仅为一个正号
		strings.Count(val, ".") > 1 || // 最多只能有一个小数点
		strings.Contains(val, " ") || // 不能包含空格
		utf8.RuneCountInString(val) != l { // 长度不符, 包含非Ascii字符
		return false
	}
	// 不允许负数
	if !isNegative && val[0] == '-' {
		return false
	}
	// 开始检查每一个字符
	var (
		hasExponent      = false // 是否进入指数部分
		hasExponentDigit = false // 指数部分是否有数字
		hasDot           = false // 基数部分是否有小数点
	)
	i := 0
	// - + 符号
	if val[i] == '-' || val[i] == '+' {
		i++
	}
	if val[i] == '0' {
		// 首位是0，第二位只能是小数点，第三位只能是数字
		i++
		if i > l-1 || val[i] != '.' {
			return false
		}
		i++
		if val[i] < '0' || val[i] > '9' {
			return false
		}
		hasDot = true
	} else {
		// 首位是必须是不为0的数字(基数部分)
		if val[i] < '1' || val[i] > '9' {
			return false
		}
		i++
	}

	val = strings.ToLower(val)
	for ; i < l; i++ {
		switch val[i] {
		case '+', '-':
			// 符号只能出现在指数部分开始的位置
			if i < 2 {
				return false
			}
			if val[i-1] == 'e' && hasExponent {
				//exponentSign = true
				continue
			}
			return false
		case '.':
			// 小数点只能在基数部分，且只能出现一次
			if hasExponent || hasDot {
				return false
			}
			hasDot = true
			continue
		case 'e':
			// 必须在基数部分之后，且只能出现一次
			if hasExponent {
				return false
			}
			hasExponent = true
			continue
		default:
			if val[i] < '0' || val[i] > '9' {
				return false
			} else {
				// 数字处理
				if hasExponent {
					hasExponentDigit = true
				}
			}
		}
	}
	// 检查基数部分是否有效（小数点后必须有数字）
	baseStr := val
	if hasExponent {
		baseStr = val[:strings.Index(val, "e")]
	}
	if hasDot {
		parts := strings.Split(baseStr, ".")
		if len(parts) != 2 {
			return false // invalid decimal point format
		}
		// 小数点后必须有至少一个数字
		if parts[1] == "" {
			return false // invalid decimal point: no digits after '.'
		}
	}
	// 最终检查
	if hasExponent && !hasExponentDigit {
		return false // exponent part has no digits
	}
	// 是否需要检查 小数位数
	if decimal > 0 {
		strDecimal, ok := _matchNumberSNToDecimal(val)
		if !ok || len(strDecimal) < 1 {
			return false
		}
		strParts := strings.Split(strDecimal, ".")
		if len(strParts) != 2 || len(strParts[1]) != decimal {
			return false
		}
	}
	return true
}

// MatchNumberSNToDecimal 匹配科学计数法表示的数字字符串，转换为十进制格式的数字字符串
//   - val 科学计数法表示的数字字符串
//   - 对于十进制格式字符串，直接返回该字符串 和 true
//   - 匹配成功，返回 十进制格式的数字字符串 和 true
//   - 匹配失败，返回 空字符串 和 false
func MatchNumberSNToDecimal(val string) (string, bool) {
	l := len(val)
	if l < 1 {
		return "", false
	}
	if l == 1 {
		if val[0] >= '0' && val[0] <= '9' {
			return val, true
		}
	}
	val = strings.ToLower(val)
	// 是否十进制格式的数字
	if !strings.Contains(val, "e") {
		if IsIntWithDecimal(val, 0, true) {
			return val, true
		}
		if IsFloatWithDecimal(val, 0, true) {
			return val, true
		}
		return "", false
	}
	if !IsFloatWithSN(val, 0, true) {
		return "", false
	}
	// 匹配成功后，分离独立逻辑函数(与IsFloatWidthSN共用)，依据指数和基数拼接出十进制格式的数字字符串
	return _matchNumberSNToDecimal(val)
}

// _matchNumberSNToDecimal 依据指数和基数拼接出十进制格式的数字字符串
//   - val 有效的十进制 或 科学计数法 表示的数字字符串(小写)
func _matchNumberSNToDecimal(val string) (string, bool) {
	if !strings.Contains(val, "e") {
		// 十进制格式的数字字符串直接返回
		return val, true
	}
	// 分割字符串
	parts := strings.Split(val, "e")
	if len(parts) != 2 {
		return val, true
	}
	baseStr := parts[0]
	exponentStr := parts[1]

	// 解析指数
	exponent, err := strconv.Atoi(exponentStr)
	if err != nil {
		return "", false
	}
	// 处理基数部分的小数点位置
	baseDotIndex := strings.Index(baseStr, ".")
	if baseDotIndex == -1 {
		baseDotIndex = len(baseStr) // 无小数点时视为在末尾
	}
	// 移除小数点得到纯数字字符串
	baseStrNoDot := strings.Replace(baseStr, ".", "", 1)

	// 计算新的小数点位置
	dotPosition := baseDotIndex + exponent
	// 使用 strings.Builder 拼接字符串
	var builder strings.Builder
	switch {
	case dotPosition <= 0:
		// 需要以0.开头，后面补零
		builder.WriteString("0.")
		builder.WriteString(strings.Repeat("0", -dotPosition))
		builder.WriteString(baseStrNoDot)
	case dotPosition >= len(baseStrNoDot):
		// 小数点在末尾之后，补零
		builder.WriteString(baseStrNoDot)
		builder.WriteString(strings.Repeat("0", dotPosition-len(baseStrNoDot)))
	default:
		// 插入小数点
		builder.WriteString(baseStrNoDot[:dotPosition])
		builder.WriteByte('.')
		builder.WriteString(baseStrNoDot[dotPosition:])
	}
	return builder.String(), true
}
